package com.anthill.events
{
import com.anthill.controllers.EventBus;
import com.anthill.events.hosts.Listener;
import com.anthill.namespaces.anthill;

import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;

import mx.utils.ObjectUtil;

[Bindable]
/**
 *  Used to add event listener for current event.
 * 
 */ 
[Event(name="event", type="com.anthill.events.AnthillEvent")]

/**
 *  AnthillEvent base class for framework events.
 *  Suppose to be bindable.
 * 
 */ 
public class AnthillEvent extends Event implements IEventDispatcher
{
	include "../Version.as";
	
	use namespace anthill;
	
	/**
     *  Constructor. 
     *  
     */ 
	public function AnthillEvent(type:String="", bubbles:Boolean=false, cancelable:Boolean=false)
	{
		super(type, bubbles, cancelable);
	}
	
	/**
	 *  Type of event that will be dispatched.
	 * 
	 *  @default blank.
	 */ 
	public var eventType:String = "";
	
	/**
	 *  Function that will be used as handler for this event.
	 * 
	 *  @default null.
	 */
	anthill var handler:Function;
	
	/**
	 *  Flag that used in <code>addEventListener</code>.
	 * 
	 *  @default false.
	 */
	public var useCapture:Boolean=false;
	
	/**
	 *  Parameter that used in <code>addEventListener</code>.
	 * 
	 *  @default 0.
	 */ 
	public var priority:int=0;
	
	/**
	 *  Flag that used in <code>addEventListener</code>.
	 * 
	 *  @default false.
	 */
	public var useWeakReference:Boolean=false;
	
	/**
	 *  Flag that indicates whether internal binding swiched on/off.
	 * 
	 *  @default false.
	 */
	public var bindable:Boolean=false;
	
	/**
     *  @private
     */
	private var dispatcher:IEventDispatcher = new EventDispatcher();
	
	/**
     *  @private
     *  Storage for the trigger property.
     */
    private var _trigger:IEventDispatcher;
    
    [Inspectable(category="General")]
	
	/**
     *  Specifies the component generating the event that triggers dispatching AnthillEvent. 
     *  
     */
    public function get trigger():IEventDispatcher
    {
        return _trigger;
    }
    
    /**
     *  @private
     */
    public function set trigger(value:IEventDispatcher):void
    {
        removeTriggerHandler();
        _trigger = value;
        addTriggerHandler();    
    }
	
	/**
     *  @private
     *  Storage for the triggerEvent property.
     */
    private var _triggerEvent:String = "";
    
    [Inspectable(category="General")]

    /**
     *  Specifies the event that triggers dispatching AnthillEvent. 
     */
    public function get triggerEvent():String
    {
        return _triggerEvent;
    }
    
    /**
     *  @private
     */
    public function set triggerEvent(value:String):void
    {
        if (_triggerEvent == value)
            return;
            
        removeTriggerHandler();
        _triggerEvent = value;
        addTriggerHandler();
    }
	
	/**
     *  @private
     */
	private function removeTriggerHandler():void
    {
        if (_trigger && _triggerEvent != "")  
            _trigger.removeEventListener(_triggerEvent, triggerHandler);
    }
	
	/**
     *  @private
     */
    private function addTriggerHandler():void
    {
        if (_trigger && _triggerEvent != "")
            _trigger.addEventListener(_triggerEvent, triggerHandler);
    }
	
	
	/**
     *  @private
     */
    private function triggerHandler(event:Event):void
    {
       dispatch();
    }
	
	/**
     *  @private
     *  Storage for the isListenerAdded property.
     */
	private var _isListenerAdded:Boolean=false;
	
	[Bindable("listenerAdded")]
	/**
	 *  Flag that indicates whether listener for this event is added.
	 * 
	 *  @default false.
	 */
	public function get isListenerAdded():Boolean
	{
		return _isListenerAdded;
	}

	/**
     *  @private
     */
	anthill function listenerAdded(v:Boolean):void
	{
		_isListenerAdded = v;
	}
	
	/**
     *  Purent EventBus object. Can be Dispathcer or Listener.
     */
	public var parent:EventBus;
	
	
	/**
     *  Self dispatch. 
     *  
     */
    public function dispatch():void
    {
		if(parent)
			parent.dispatchEvent(this);      
    }
    
    /**
     *  Start listening AnthillEvent.
     */
	public function startListening():void
	{
		// anthill event should have parent Listener
		if(!parent && !(parent is Listener))
			throw(new Error("Event parent should be Listener."));
			
		// anthill event should have eventType
		if(eventType == "")
			throw(new Error("eventType can't be blank."));
			
		if(!isListenerAdded)
		{
			//add event listener for manual handler
			if(handler != null)
			{
				parent.addEventListener(eventType, handler, useCapture, priority, useWeakReference);
				listenerAdded(true);
			}
				// add internal event listener if binding configured	
			if(bindable)
			{
				parent.addEventListener(eventType, bindingHandler, false, int.MAX_VALUE);
				listenerAdded(true);
			}
						
		}
	}
	
	/**
     *  Stop listening AnthillEvent.
     */
	public function stopListening():void
	{
		// anthill event should have parent Listener
		if(!parent && !(parent is Listener))
			throw(new Error("Event parent should be Listener."));
			
		// anrhill event should have eventType
		if(eventType == "")
			throw(new Error("eventType can't be blank."));
		
		if(isListenerAdded)
		{
			//add event listener for manual handler
			if(handler != null)
				parent.removeEventListener(eventType, handler, useCapture);
			// add internal event listener if binding configured	
			if(bindable)
				parent.removeEventListener(eventType, bindingHandler);
				
			listenerAdded(false);
		}
	}
	
		/**
     *  @private
     */
	protected function bindingHandler(event:AnthillEvent):void
	{
	
		// update properties from received event
		var o:Object = ObjectUtil.getClassInfo(event);
		for each(var p:QName in o.properties)
		{
			// property can be read-only
			try{
				// skip updating standard properties 
				if(p.localName != "handler" && p.localName != "useCapture" &&
				   p.localName != "priority" && p.localName != "useWeakReference" &&
				   p.localName != "bindable" && p.localName != "dispatcher" &&
				   p.localName != "eventType")
				   
				this[p.localName] = event[p.localName];
				
			}catch(e:Error){}
		}
	}
	
	
	/**
     *  @private
     */
	public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false) : void
	{
		// if type is "event" it means that system tries to add event listener 
		// and it should be redirected to Listener.  
		if(type=="event")
			handler = listener;
		else
			// Used for bindings
			dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
	}
	
	/**
     *  @private
     */
	public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false) : void
	{
		dispatcher.removeEventListener(type, listener, useCapture);
	}
	
	/**
     *  @private
     */
	public function dispatchEvent(event:Event) : Boolean
	{
		return dispatcher.dispatchEvent(event);
	}
	
	/**
     *  @private
     */
	public function hasEventListener(type:String) : Boolean
	{
		return dispatcher.hasEventListener(type);
	} 

	/**
     *  @private
     */
	public function willTrigger(type:String) : Boolean
	{
		return dispatcher.willTrigger(type);
	}
}
}